package cn.iocoder.yudao.framework.common.util.object;

import cn.hutool.core.bean.BeanUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.ConvertUtil;
import cn.iocoder.yudao.framework.common.util.PropertyUtil;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;

import java.beans.BeanInfo;
import java.beans.Beans;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Bean 工具类
 *
 * 1. 默认使用 {@link cn.hutool.core.bean.BeanUtil} 作为实现类，虽然不同 bean 工具的性能有差别，但是对绝大多数同学的项目，不用在意这点性能
 * 2. 针对复杂的对象转换，可以搜参考 AuthConvert 实现，通过 mapstruct + default 配合实现
 *
 * @author 芋道源码
 */
public class BeanUtils {

    public static <T> T toBean(Object source, Class<T> targetClass) {
        return BeanUtil.toBean(source, targetClass);
    }

    public static <S, T> List<T> toBean(List<S> source, Class<T> targetType) {
        if (source == null) {
            return null;
        }
        return CollectionUtils.convertList(source, s -> toBean(s, targetType));
    }

    public static  <S, T> PageResult<T> toBean(PageResult<S> source, Class<T> targetType) {
        if (source == null) {
            return null;
        }
        return new PageResult<>(toBean(source.getList(), targetType), source.getTotal());
    }

    /**
     * bean转map
     *
     * @param params
     * @return
     * @throws Exception
     */
    public static Map<String, Object> conventAllToMap(Object... params) {
        Map<String, Object> param = new HashMap<String, Object>();
        for (Object obj : params) {
            if (obj instanceof Map) {
                param.putAll((Map<String, Object>) obj);
                continue;
            }
            Class<? extends Object> clazz = obj.getClass();
            Method[] methods = clazz.getMethods();
            Pattern pattern = Pattern.compile("^get*");
            Matcher matcher = null;
            for (Method m : methods) {
                matcher = pattern.matcher(m.getName());
                if (matcher.find()) {
                    String key = m.getName().replace("get", "");
                    key = (key.charAt(0) + "").toLowerCase().concat(key.substring(1));
                    try {
                        Object res = m.invoke(obj);
                        param.put(key, res);
                    } catch (Exception e) {
                    }
                }
            }
        }
        return param;
    }

    /**
     * 将非空的属性转换到map中
     *
     * @param params
     * @return
     * @throws Exception
     */
    public static Map<String, Object> conventToMap(Object... params) {
        Map<String, Object> param = new HashMap<String, Object>();
        for (Object obj : params) {
            Class<? extends Object> clazz = obj.getClass();
            Method[] methods = clazz.getMethods();
            Pattern pattern = Pattern.compile("^get*");
            Matcher matcher = null;
            for (Method m : methods) {
                matcher = pattern.matcher(m.getName());
                if (matcher.find()) {
                    String key = m.getName().replace("get", "");
                    key = (key.charAt(0) + "").toLowerCase().concat(key.substring(1));
                    Object res;
                    try {
                        res = m.invoke(obj);
                        if (res != null) {
                            param.put(key, res);
                        }
                    } catch (Exception e) {
                    }
                }
            }
        }
        return param;
    }

    /**
     * 将bean转换到map,不包含class属性
     *
     * @param obj
     * @return
     * @throws Exception
     */
    public static Map<String, Object> convertBeanToMap(Object obj) throws Exception {
        if (obj == null) {
            return null;
        }
        Map<String, Object> map = new HashMap<String, Object>();

        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            // 过滤class属性
            if (!key.equals("class")) {
                // 得到property对应的getter方法
                Method getter = property.getReadMethod();
                Object value = getter.invoke(obj);

                map.put(key, value);
            }

        }
        return map;
    }

    /**
     * 将obj对应的property属性的值设置为 value
     *
     * @param obj 对象
     * @param property 属性
     * @param value 属性值
     * @param convertType 自动转换类型
     * @param ignoreNullValue 是否自动忽略null
     * @return
     */
    private static boolean copyProperty(Object obj, String property, Object value, boolean convertType,
                                        boolean ignoreNullValue) {
        if (obj == null) {
            throw new IllegalArgumentException("no bean specified");
        }
        if (property == null) {
            throw new IllegalArgumentException("no property specified");
        }
        if (ignoreNullValue && value == null) {
            return true;
        }

        if (obj instanceof Map) {
            return invokeSetter(obj, property, value, convertType, ignoreNullValue);
        }

        StringTokenizer st = new StringTokenizer(property, ".");
        if (st.countTokens() == 0) {
            return false;
        }

        Object current = obj;
        try {
            while (st.hasMoreTokens()) {
                String currentPropertyName = st.nextToken();
                if (st.hasMoreTokens()) {
                    current = invokeGetter(current, currentPropertyName);
                } else {
                    try {
                        invokeSetter(current, currentPropertyName, value, convertType, ignoreNullValue);
                    } catch (Exception e) {
                        return false;
                    }
                }
            }
            return true;
        } catch (NullPointerException e) {
            return false;
        }
    }

    /**
     * 获得obj对象property属性的值
     *
     * @param obj 对象
     * @param property 属性
     * @return
     */
    private static Object getProperty(Object obj, String property) {
        if (obj == null) {
            throw new IllegalArgumentException("no bean specified");
        }
        if (property == null) {
            throw new IllegalArgumentException("no property specified");
        }

        if (obj instanceof Map) {
            return ((Map) obj).get(property);
        }

        StringTokenizer st = new StringTokenizer(property, ".");
        if (st.countTokens() == 0) {
            return null;
        }

        Object result = obj;

        try {
            while (st.hasMoreTokens()) {
                String currentPropertyName = st.nextToken();
                if (result != null) {
                    result = PropertyUtil.getProperty(result, currentPropertyName);
                }
            }
            return result;
        } catch (NullPointerException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取obj对象的properties属性的值
     *
     * @param obj
     * @param properties
     * @return
     */
    private static Map getProperties(Object obj, String[] properties) {
        if (obj == null) {
            throw new IllegalArgumentException("no bean specified");
        }
        if (properties == null) {
            throw new IllegalArgumentException("no priperties specified");
        }
        Map result = new LinkedHashMap();
        for (int i = 0; i < properties.length; i++) {
            Object value = getProperty(obj, properties[i]);
            result.put(properties[i], value);
        }
        return result;
    }

    /**
     * get property from object
     *
     * @param obj target object
     * @param property target property
     * @return
     */
    private static Object invokeGetter(Object obj, String property) {
        if (obj instanceof Map) {
            return ((Map) obj).get(property);
        } else {
            return PropertyUtil.getProperty(obj, property);
        }
    }

    /**
     * inject value into object
     *
     * @param obj target object
     * @param property target property
     * @param value injected object
     * @param autoConvert 是否需要自动转换类型
     * @param ingoreNullValue 是否自动忽略NULL值
     * @return
     */
    private static boolean invokeSetter(Object target, String property, Object value, boolean autoConvert,
                                        boolean ingoreNullValue) {
        if (target instanceof Map) {
            ((Map) target).put(property, value);
            return true;
        }
        Object newValue = null;
        if (autoConvert) {
            Class type = PropertyUtil.getPropertyType(target.getClass(), property);
            if (Beans.isInstanceOf(value, type)) {
                newValue = value;
            } else if (value instanceof String) {
                newValue = ConvertUtil.convert(type, (String) value);
            } else {
                newValue = value;
            }
        } else {
            newValue = value;
        }
        if (!ingoreNullValue || newValue != null) {
            boolean r = PropertyUtil.setProperty(target, property, newValue);
            return r;
        }
        return true;
    }

    /**
     * 获取所有属性名称
     *
     * @param obj
     * @return
     */
    private static String[] getPropertyNames(Object obj) {
        if (obj == null) {
            throw new IllegalArgumentException("no bean specified");
        }
        if (obj instanceof Map) {
            Object[] keys = ((Map) obj).keySet().toArray();
            String[] results = new String[keys.length];
            for (int i = 0; i < keys.length; i++) {
                results[i] = keys[i] + "";
            }
            return results;
        }
        String[] result = PropertyUtil.getPropertyNames(obj.getClass());
        int index = Arrays.binarySearch(result, "class");

        if (index > 0) {
            if (result.length == 1) {
                return new String[0];
            }
            String[] newResult = new String[result.length - 1];
            System.arraycopy(result, 0, newResult, 0, index);
            if (index < result.length) {
                System.arraycopy(result, index + 1, newResult, index, result.length - index - 1);
            }
            return newResult;
        } else {
            return result;
        }
    }

    /**
     * 该方法过滤掉了class属性，可用于dubbo传输
     * @param obj
     * @return
     */
    // Bean --> Map 1: 利用Introspector和PropertyDescriptor 将Bean --> Map
    public static Map<String, Object> transBean2Map(Object obj) {

        if(obj == null){
            return null;
        }
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();

                // 过滤class属性
                if (!key.equals("class")) {
                    // 得到property对应的getter方法
                    Method getter = property.getReadMethod();
                    Object value = getter.invoke(obj);

                    map.put(key, value);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return map;

    }

}
